package com.cway;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ReUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.ds.simple.SimpleDataSource;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import net.coobird.thumbnailator.Thumbnails;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    static JSONObject config;
    static JSONObject configTs;
    static String basePath;

    //    图床配置文件
    static final String configJson = "config.json";
    //    网络任务配置
    static final String configTask = "configTask.json";
    //    数据库配置
    static final String configDb = "config.db";

    static {
        //       获取外置配置文件 config.json等 放在jar包所在路径
        String path = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        basePath = path.substring(0, path.lastIndexOf("/") + 1);
        init();
        path = basePath + configJson;
        config = JSONUtil.parseObj(new FileReader(path).readString());
        configTs = JSONUtil.parseObj(new FileReader(basePath + configTask).readString());
    }
//    图片临时缓存文件夹
    static String tempPath = basePath + "temp/";

    public static void main(String[] args) throws IOException, SQLException {
        for (String arg : args) {
//            如果传入的是txt文件，则执行将其内容转为json键值对操作
            if (arg.endsWith(".txt")) {
                System.out.println(Util.pairToJson(new FileReader(arg).readString()));
                continue;
            }
            if (arg.endsWith(".md")) {
                writer(arg);
                continue;
            }
            if (arg.startsWith("http")) {
                if (!FileUtil.exist(tempPath)) {
                    FileUtil.mkdir(tempPath);
                }
                try {
                    File file = HttpUtil.downloadFileFromUrl(arg, tempPath);
                    arg = file.getAbsolutePath();
                } catch (Exception e) {
//                    若图片下载报错 判断是不是302跳转
                    String redictURL = Util.getRedirectUrl(arg);
                    if (redictURL != null && !Objects.equals(arg, redictURL)) {
                        File file = HttpUtil.downloadFileFromUrl(redictURL, tempPath);
                        arg = file.getAbsolutePath();
                    } else {
                        System.out.println("图片重定向下载出错");
                    }
                }
            }
            uploadImg(arg);
        }
        task();
    }

    /**
     * 初始化配置文件
     */
    private static void init() {
        Dict dict = Dict.create()
                .set(configDb, basePath + configDb)
                .set(configJson, basePath + configJson)
                .set(configTask, basePath + configTask);
        for (Map.Entry<String, Object> entry : dict.entrySet()) {
            if (!FileUtil.exist(entry.getValue().toString())) {
                InputStream stream = ResourceUtil.getStream(entry.getKey());
                FileWriter fileWriter = new FileWriter(entry.getValue().toString());
                fileWriter.writeFromStream(stream);
            }
        }
    }

    public static void writer(String path) throws SQLException {
        String md = new FileReader(path).readString();
        String name = FileUtil.getName(path);
        String title = Util.getFileName(name);
        String content = Util.md2Html(md);
        Map<String, Object> runMap = Util.setRunMap(configTs);
        runMap.put("title", title);
        runMap.put("content", content);
        JSONObject write = configTs.getJSONObject("write");
        for (Map.Entry<String, Object> entry : write.entrySet()) {
            Object value = entry.getValue();
            if (entry.getKey().endsWith("_md")) {
                runMap.put("content", md);
            }
            JSONArray array = JSONUtil.parseArray(value);
            post(array, runMap, null, null);
        }
    }

    /**
     * 执行网络任务 每天一次
     *
     * @throws SQLException sql执行异常
     */
    public static void task() throws SQLException {
        //        写入执行时间 保证一天只执行一次
        String format = "yyyy-MM-dd HH:mm:ss";
        if (configTs.containsKey("lastExec")) {
            String lastExec = configTs.getStr("lastExec").substring(0, 10);
            if (new DateTime().toString("yyyy-MM-dd").equalsIgnoreCase(lastExec)) {
                return;
            } else {
                configTs.set("lastExec", new DateTime().toString(format));
            }
        } else {
            configTs.set("lastExec", new DateTime().toString(format));
        }
        Map<String, Object> runMap = Util.setRunMap(configTs);
        JSONObject task = configTs.getJSONObject("task");
        for (Map.Entry<String, Object> entry : task.entrySet()) {
            Object value = entry.getValue();
            JSONArray array = JSONUtil.parseArray(value);
            post(array, runMap, null, null);
        }
        FileWriter writer = new FileWriter(basePath + configTask);
        writer.write(configTs.toStringPretty());
//      删除缓存文件夹
        if (FileUtil.exist(tempPath))
            FileUtil.del(tempPath);
    }

    /**
     * 上传图床
     *
     * @param arg 图片路径
     * @throws IOException  IO异常
     * @throws SQLException SQL异常
     */
    public static void uploadImg(String arg) throws IOException, SQLException {
        //        获取图床类型
        String current = config.getByPath("current").toString();
        Object bed = config.getByPath("bed." + current);
//        储存运行中的临时变量
        Map<String, Object> runMap = Util.setRunMap(config);
        //            文件信息
        File file = FileUtil.file(arg);
//        秒传
        if (config.containsKey("db")) {
            SimpleDataSource dataSource = new SimpleDataSource("jdbc:sqlite:" + basePath + "/config.db", "", "");
            Db db = Db.use(dataSource);
            List<Entity> by = db.findBy("image", "md5", SecureUtil.md5(file));
            if (by.size() > 0) {
                String url = by.get(0).toBeanIgnoreCase(Image.class).getUrl();
                System.out.println(url);
                return;
            }
        }
        int dot = arg.lastIndexOf(".");
//            .jpg
        String ext = arg.substring(dot);
//            path
        String imgPath = arg.substring(0, dot);
//        图片压缩 仅在非gif时压缩
        File temp = null;
        if (config.containsKey("zip") && !ext.toLowerCase(Locale.ROOT).endsWith("gif")) {
            Float zip = config.getFloat("zip");
            Thumbnails.of(arg)
                    .scale(1f)
                    .outputQuality(zip)
                    .toFile(imgPath + "_t" + ext);
            temp = FileUtil.file(imgPath + "_t" + ext);

        }
//        获取图床配置
        JSONArray array = JSONUtil.parseArray(bed);
//        发送请求
        post(array, runMap, file, temp);
//            删除临时文件
        if (config.containsKey("zip") && FileUtil.exist(imgPath + "_t" + ext)) {
            FileUtil.del(imgPath + "_t" + ext);
        }
    }

    /**
     * 处理请求信息
     *
     * @param jsonArray 请求
     * @param runMap    变量Map
     * @param file      原始文件
     * @param temp      压缩后文件
     * @throws SQLException sql执行异常
     */
    public static void post(JSONArray jsonArray, Map<String, Object> runMap, File file, File temp) throws SQLException {
        String md5 = null;
        String uploadMd5 = null;
        long size = 0;
        String fileName = null;
//        文件存在的话才获取文件信息
        if (file != null) {
            size = file.length();
            fileName = file.getName();
            md5 = SecureUtil.md5(file);
            if (temp != null) {
//            实际上传文件md5
                uploadMd5 = SecureUtil.md5(temp);
                size = temp.length();
                file = temp;
            }
        }

        for (Object o : jsonArray) {
            JSONObject obj = JSONUtil.parseObj(o);
//            有效性判断 如果use值为false则跳过该请求
            boolean use = obj.containsKey("use");
            if (use && "false".equalsIgnoreCase(obj.get("use").toString()))
                continue;
//            文件有效时才执行gt lt判断
            if (size > 0) {
//            大于gt值执行
                if (obj.containsKey("gt")) {
                    Long gt = obj.getLong("gt");
                    if (size < gt)
                        continue;
                }
//            小于等于lt值执行
                if (obj.containsKey("lt")) {
                    Long lt = obj.getLong("lt");
                    if (size >= lt)
                        continue;
                }
            }
//            时间戳
            runMap.put("timestamp", System.currentTimeMillis());
//            URL(必选)
            String url = obj.getStr("url");
            if (url.contains("($")) {
                List<String> resultFindAll = ReUtil.findAll("\\((\\$.*?)\\)", url, 1, new ArrayList<>());
                for (String s : resultFindAll) {
                    url = url.replace("(" + s + ")", runMap.get(s.substring(1)).toString());
                }
            }
//            请求类型(可选)
            String type = obj.containsKey("type") ? obj.getStr("type") : "get";
//            头信息(可选)
            Map<String, List<String>> headerMap = new HashMap<>();
            if (obj.containsKey("header")) {
                String header = obj.getStrEscaped("header");
                JSONObject headerObj = JSONUtil.parseObj(header);
                for (Map.Entry<String, Object> entry : headerObj.entrySet()) {
                    String value = entry.getValue().toString();
                    if (value.startsWith("$")) {
                        value = runMap.get(value.substring(1)).toString();
                    }
                    value = Util.getDollar(runMap, value);
                    if ("Cookie".equalsIgnoreCase(entry.getKey())) {
                        runMap.put("cookie", value);
                    }
                    if ("User-Agent".equalsIgnoreCase(entry.getKey())) {
                        runMap.put("ua", value);
                    }
                    String[] split = value.split(";");
                    headerMap.put(entry.getKey(), Arrays.asList(split));
                }
            }
//            参数(可选)
            Map<String, Object> paramMap = new HashMap<>();
            if (obj.containsKey("param")) {
                JSONObject param = obj.getJSONObject("param");
                for (Map.Entry<String, Object> entry : param.entrySet()) {
                    Object value = entry.getValue();
                    String valueStr = value.toString();
                    if (valueStr.startsWith("$")) {
                        value = runMap.get(valueStr.substring(1));
                    }
                    value = Util.getDollar(runMap, value.toString());
                    paramMap.put(entry.getKey(), value);
                }
            }
//            文件有效时才处理文件域
            if (size > 0) {
//            加入上传域(可选)
                if (obj.containsKey("fileFiled")) {
                    paramMap.put(obj.getStr("fileFiled"), file);
                }
//            file base64
                if (obj.containsKey("fileBase64")) {
                    url += fileName;
                    String encode = Base64.encode(file);
                    paramMap.put(obj.getStr("fileBase64"), encode);
                }
            }
//            发送请求
            HttpResponse result;
            if ("post".equalsIgnoreCase(type)) {
                result = HttpRequest.post(url)
                        .header(headerMap)
                        .form(paramMap)
                        .execute();
            } else {
                result = HttpRequest.get(url)
                        .header(headerMap)
                        .form(paramMap)
                        .execute();
            }

//            处理结果
            String resultObj = "";
            String body = result.body();
            boolean successFlag = false;
            if (obj.containsKey("successFlag")) {
                successFlag = body.contains(obj.getStr("successFlag"));
            }
//            添加额外成功标识如：successFlag1,successFlag2等一直到10,返回结果中其中一个被包含则可用getBy解析数据
            if (!successFlag) {
                for (int i = 1; i <= 10; i++) {
                    String successKey = "successFlag" + i;
                    if (obj.containsKey(successKey)) {
                        successFlag = body.contains(obj.getStr(successKey));
                        if (successFlag)
                            break;
                    }
                }
            }
//            路径查找可以多路径以小写逗号为分隔符,从左到右依次查询，查询到停止,例如sm.ms的同一张图片第一次上传是data.url,之后就是data.image
            if (obj.containsKey("getByPath") && successFlag) {
                String byPath = obj.getStr("getByPath");
                String[] split = byPath.split(",");
                JSON parse = JSONUtil.parse(body);
                for (String s : split) {
                    if (parse.getByPath(s) != null) {
                        resultObj = parse.getByPath(s).toString();
                        break;
                    }
                }
            } else if (obj.containsKey("getByReg") && successFlag) {
                String byReg = obj.getStr("getByReg");
                Pattern pattern = Pattern.compile(byReg);
                Matcher matcher = pattern.matcher(body);
                if (matcher.find()) {
                    resultObj = matcher.group(1);
                }
            } else if (obj.containsKey("all")) {
                resultObj = body;
            } else {
                resultObj = "返回值中无匹配规则的成功标识:" + body;
            }
//            前追加
            if (obj.containsKey("before")) {
                resultObj = obj.getStr("before") + resultObj;
            }
//            后追加
            if (obj.containsKey("after")) {
                resultObj += obj.getStr("after");
            }
//            别名结果(可选)
            if (obj.containsKey("alis")) {
                runMap.put(obj.getStr("alis"), resultObj);
            }
//            输出(可选)
            if (obj.containsKey("out")) {
                String out = obj.getStr("out");
                if (out.startsWith("$")) {
                    out = runMap.get(out.substring(1)).toString();
                }
                out = Util.getDollar(runMap, out);
//                若存在变量使用变量值
                if (obj.getStr("out").startsWith("$") || obj.getStr("out").contains("($")) {
                    resultObj = out;
                }
                if (successFlag) {
                    System.out.println(resultObj);
                    if (config.containsKey("db") && file != null && resultObj.startsWith("http")) {
                        SimpleDataSource dataSource = new SimpleDataSource("jdbc:sqlite:" + basePath + "/config.db", "", "");
                        Db db = Db.use(dataSource);
                        db.insert(Entity.create("image").set("md5", md5)
                                .set("url", resultObj)
                                .set("umd5", uploadMd5)
                                .set("uname", fileName));
                    }
                } else {
                    System.out.println("返回值中无成功标识:" + body);
                }
            }
        }
    }

}